#include "ProcesoPrincipal.h"
#include <sstream>
#include <signal.h>

#define CYAN "\033[0;36m"
#define RED "\033[31m"
#define NO_COLOUR "\033[0m"

bool ProcesoPrincipal::salir =false;

void ProcesoPrincipal::enviarMensaje(int jugador, Mensaje* m){
	obtenerFifo(jugador).escribir(m->getBuffer(), m->size());
}

Fifo ProcesoPrincipal::obtenerFifo(int numeroJugador){
	std::stringstream s;
	s << numeroJugador;
	return Fifo(s.str().c_str());
}

void ProcesoPrincipal::procesarComandoServer(MensajeComando *m){
	std::string comando = m->getComando();

	if(comando[0] == 'l'){
		Lock l(std::dbg);
		std::dbg << "Jugadores:\n";
		//listo jugadores
		std::map<int,Jugador>::iterator it;
		for(it = jugadores.begin(); it!= jugadores.end(); it++){
			if(estaJugando((*it).first))
				std::cout << RED;
			else std::cout << CYAN;
			std::cout << (*it).second.getNombre() << ":" << (*it).second.getId() << "\n";			
			std::cout << NO_COLOUR;
		}
	}

	if(comando[0] == 'p'){
		Lock l(std::dbg);
		int jugador = atoi(comando.c_str()+1);
		if(!estaJugando(jugador))
			std::cout << "El jugador " << jugador << " no esta jugando.\n";

		else if(existeJugador(jugador)){
			int oponente = jugadores[jugador].getOponente();
			std::cout << "\nJugador       : " << jugadores[jugador].getNombre() << " id:" << jugadores[jugador].getId() << " (" << jugadores[jugador].getTablero(0).getCantidadBarcos() << " barcos restantes)\n";
			std::cout <<   "Jugando contra: " << jugadores[oponente].getNombre() << " id:" << jugadores[oponente].getId() << " (" << jugadores[oponente].getTablero(0).getCantidadBarcos() << " barcos restantes)\n";
		}
		else{
			std::cout << "No existe el jugador.\n";
		}
	}

	if(comando[0] == 'd'){
		Lock l(std::dbg);
		int jugador = atoi(comando.c_str()+1);
		if(!existeJugador(jugador)){
			std::dbg << "No existe el jugador.\n";
			return;
		}
		MensajeDesconectar md;
		if(estaJugando(jugador)){
			std::dbg << "El cliente estaba jugando. Finalizo el juego.\n";
			int oponente = jugadores[jugador].getOponente();
			
			enviarMensaje(jugador, &md); //Finaliza el juego
			enviarMensaje(oponente, &md);

			juegos.erase(juegos.find(oponente)); //Elimino el juego
			delete juegos[jugador];
			juegos.erase(juegos.find(jugador));
		}
		jugadores.erase(jugador);
		enviarMensaje(jugador, &md); //Finaliza el cliente
		std::dbg << "Jugador eliminado.\n";
		
	}

	if(comando[0] == 'q'){
		kill(pidTeclado, SIGKILL);
		finalizar();
	}
}

void ProcesoPrincipal::procesarMensaje(Mensaje *m){

	std::map<int, std::string>::iterator it;

	if(m->getFuente() == ID_SERVIDOR){
		procesarComandoServer((MensajeComando*)m);
		return;
	}

	if(estaJugando(m->getFuente())){
		juegos[m->getFuente()]->recibirMensaje(m);
		return;
	}

	switch(m->getTipo()){
	case MENSAJE_CONECTAR:
	{
		Lock l(std::dbg);
		std::string nombre = ((MensajeConectar*)m)->getNombre();
		std::dbg << "Nuevo jugador Conectado: " <<  nombre << " con identificador " << m->getFuente() << "\n";
		Jugador j;
		//Agrego al jugador a la lista de jugadores
		j.setNombre(nombre);
		j.setId(m->getFuente());
		jugadores[m->getFuente()] = j;
	}

	break;
	case MENSAJE_GET_JUGADORES:
	{{Lock l(std::dbg);
			std::dbg << "Me piden la lista de jugadores: " <<  "\n";}
		std::list<MensajeListaJugadores> lista = MensajeListaJugadores::generarMensajes(ID_SERVIDOR, m->getFuente(), this);
		for(std::list<MensajeListaJugadores>::iterator it=lista.begin(); it!=lista.end(); it++)
			enviarMensaje(m->getFuente(),&(*it));
	}
	break;
	
	case MENSAJE_INVITACION:
		procesarInvitacion((MensajeInvitacion*)m);
		break;
		
	case MENSAJE_DESCONECTAR:
	{{Lock l(std::dbg);
				std::dbg << "Me piden desconectarse\n";}
			if(jugadores.find(m->getFuente()) != jugadores.end()){
				std::cout << "No esta jugando\n";
				jugadores.erase(jugadores.find(m->getFuente())); //Borro al jugador de la lista
			}
	}
	default:
		break;
	}

}

void ProcesoPrincipal::procesarInvitacion(MensajeInvitacion *m){
	{Lock l(std::dbg);
		std::dbg << "El jugador "<< m->getFuente() << " envía un mensaje de invitación a "<< ((MensajeInvitacion*)m)->getDestinatario() <<  "\n";}

	int origen,destino;
	origen=m->getFuente();
	destino = ((MensajeInvitacion*)m)->getDestinatario();

	if(jugadores.find(destino) == jugadores.end()){
		Lock l(std::dbg);
		std::dbg << "No se puede enviar una invitacion a un jugador inexistente.\n";
		MensajeNotificacion mn("No se puede enviar una invitacion a un jugador inexistente.");
		enviarMensaje(origen, &mn);
		return;
	}
	if(origen == destino){
		Lock l(std::dbg);
		std::dbg << "No se puede enviar una invitacion de " << origen << " a " << origen << "\n";

		MensajeNotificacion mn("No podes invitarte a vos mismo.");
		enviarMensaje(origen, &mn);

		return;
	}
	if(estaJugando(origen)){
		Lock l(std::dbg);
		std::dbg << "No se puede enviar una invitacion si se está jugando." << origen <<"\n";
		return;
	}
	if(estaJugando(destino)){
		Lock l(std::dbg);
		std::dbg << "No se puede enviar una invitacion a un jugador que está jugando." << destino <<"\n";

		MensajeNotificacion mn("No se puede enviar una invitacion a un jugador que está jugando.");
		enviarMensaje(origen, &mn);

		return;
	}

	invitaciones[origen] = destino;
	enviarMensaje(destino, m);

	if(invitaciones[destino] == origen){
		//se puede comenzar a jugar, los dos
		//jugadores se invitaron mutuamente
		Lock l(std::dbg);
		std::dbg << "Los jugadores " << origen << " y " << destino << " comienzan el juego\n";
		Juego * juego= new Juego(origen, destino, this);
		jugadores[origen].setOponente(destino);
		jugadores[destino].setOponente(origen);
		juegos[origen] = juego;
		juegos[destino]= juego;
		MensajeComenzarJuego comienzo1(origen, jugadores[origen].getNombre());
		MensajeComenzarJuego comienzo2(destino, jugadores[destino].getNombre());
		
		enviarMensaje(destino, &comienzo1);
		enviarMensaje(origen, &comienzo2);
	}
}

static void finalizarTeclado(int signal){
	ProcesoPrincipal::setSalida();
}

void ProcesoPrincipal::run(){
	{Lock l(std::dbg);
		std::dbg << "Abro el fifo y espero mensajes\n";	}

	fifo.abrir("FIFOSERVER");

	int pid=fork();
	if(pid==0){
		signal(SIGUSR1, finalizarTeclado);
		//Proceso el teclado
		procesarTeclado();
		exit(0);
	}
	pidTeclado = pid;


	while(!salir){
		Mensaje m;

		{Lock l(std::dbg);
			std::dbg << "Espero mensaje:\n";}
		if(fifo.leer(m.getBuffer(), m.size()) > 0){
			{Lock l(std::dbg);
				std::dbg << "EL servidor recibe el siguiente mensaje:";
				m.mostrar();}
			//Procesar el mensaje
			procesarMensaje(&m);
		}else{
			{Lock l(std::dbg);
				std::dbg << "Error: Mensaje Erroneo\n";}
		}
	}
	fifo.borrar();
}

ProcesoPrincipal::~ProcesoPrincipal(){
	std::map<int,Jugador>::iterator it;
	for(it=jugadores.begin();it!=jugadores.end();it++){
		MensajeDesconectar md;
		int jugador = (*it).first;
		if(estaJugando(jugador)){
			int oponente = (*it).second.getOponente();

			enviarMensaje(jugador, &md); //Finaliza el juego
			enviarMensaje(oponente, &md);

			juegos.erase(juegos.find(oponente)); //Elimino el juego
			delete juegos[jugador];
			juegos.erase(juegos.find(jugador));
		}
		enviarMensaje(jugador, &md); //Finaliza el cliente
	}
}
